﻿using System;
using System.Globalization;
using System.Text;

namespace Stauffware.Common.Core
{
	//#################################################################################
	/// <summary>
	/// Abstract base class for integer parsers.
	/// </summary>
	/// <typeparam name="Tvalue">the type of the parsed result value (int, short, long, etc.)</typeparam>
	/// <typeparam name="Tparse">the type of the returned Parse object</typeparam>

#if false
	public abstract class IntegerParser<T>  : NumberParser<T>  where T : struct
	{
		/// <summary>
		/// Enum bits reserved for NumberParseOptions.  
		/// "Derived" parse option enums must not redefine these.
		/// </summary>
		internal const int Reserved_ParseOptions_Bits = 0xFFFF00;

		/// <summary>
		/// What to use when there isn't a NumberParseOptions parameter.
		/// </summary>
		public const NumberParseOptions DefaultParseOptions = 
			NumberParseOptions.AllowWhiteSpace | NumberParseOptions.ParseEntireInput | 
			NumberParseOptions.AllowExponent | NumberParseOptions.AllowDecimalPoint | 
			NumberParseOptions.AllowLeadingSign | NumberParseOptions.AllowTrailingSign | 
			NumberParseOptions.AllowGrouping;

		/// <summary>
		/// Gets the maximum number of digits (base 10) that a number can have.
		/// </summary>
		protected abstract int MaxDigits { get; }

		/// <summary>
		/// Gets whether this parser's target type is signed.
		/// </summary>
		protected abstract bool Signed { get; }

		/// <summary>
		/// Creates a Parse object of the proper class for this Parser.
		/// </summary>
		/// <returns></returns>
		protected abstract IntegerParse<T> CreateParseObject ();

		/// <summary>
		/// Attempts a parse of a normalized integer string.
		/// </summary>
		/// <param name="str">
		///		The string, which must contain 1 or more decimal digits, optionally 
		///		preceded by a '-'.  No whitespace, no separators, no exponents.
		/// </param>
		/// <param name="value">receives the parsed value</param>
		/// <returns>true on success, false on failure</returns>
		protected abstract bool TryParse (string str, out T value);

		//-----------------------------------------------------------------------------
		/// <summary>
		/// Parses an integer.
		/// </summary>
		/// <param name="input">the string to be parsed</param>
		/// <param name="options">parsing options</param>
		/// <param name="ifp">formatting info, or null</param>

		protected IntegerParse<Tvalue> _Parse (string input, NumberParseOptions options, IFormatProvider ifp)
		{
			Require.NotNull(input);

			if (!this.Signed)
				options &= ~NumberParseOptions.AllowAnySign;

			NumberLexer lexer = new NumberLexer(options, null, MaxDigits, 3);

			bool lexedAnumber = lexer.LexNumber(input);

			IntegerParse<Tvalue> parse = this.CreateParseObject();
			parse._parsedText = input.Substring(0, lexer.CharsLexed);
			parse._errorMessage = lexer.ErrorMessage;

			if (lexedAnumber)
			{
				string digits = lexer.DigitsLeft;

				if (options.HasFlag(NumberParseOptions.AllowExponent))
					if (!this.AdjustDigitsForExponent(parse, lexer, ref digits))
						return parse;

				if (lexer.IsNegative)
					digits = "-" + digits;

				Tvalue tempValue;
				if (this.TryParse(digits, out tempValue))
					parse._value = tempValue;
				else
				{
					// NumberLexer has already checked for all possible syntax errors,
					// so the only thing left that could go wrong is a numeric overflow.
					parse._errorMessage = "number is too large";
				}
			}

			return parse;
		}

		//-----------------------------------------------------------------------------
		/// <summary>
		/// Adjusts a string of digits to compensate for an exponent 
		/// (specified in NumberLexer).
		/// </summary>
		/// <param name="parse">the Parse object</param>
		/// <param name="lexer">the result of lexing the number string</param>
		/// <param name="digits">the digits to adjust</param>
		/// <returns></returns>

		protected bool AdjustDigitsForExponent (IntegerParse<Tvalue> parse, NumberLexer lexer, ref string digits)
		{
			int exponent = 1;
			if (!string.IsNullOrEmpty(lexer.ExponentDigits))
				exponent = int.Parse(lexer.ExponentDigits);

			if (exponent < 0)
			{
				parse._errorMessage = "negative exponents not allowed";
				return false;
			}

			if (!string.IsNullOrEmpty(lexer.DigitsRight))
			{
				if (lexer.DigitsRight.Length >= exponent)
				{
					parse._errorMessage = "invalid fractional digits";
					return false;
				}
				digits += lexer.DigitsRight;
				exponent -= lexer.DigitsRight.Length;
			}

			digits = digits.PadRight(digits.Length + exponent, '0');
			return true;
		}		
	}
#else
	public abstract class IntegerParser<Tvalue, Tparse> : NumberParser<Tvalue, Tparse>
		where Tvalue : struct
		where Tparse : NumberParse<Tvalue>, new()
	{
		///// <summary>
		///// Constructs a parser that uses NumberParseOptions.DefaultParseOptions.
		///// </summary>
		//public IntegerParser ( ) : base(DefaultParseOptions, null) { }

		///// <summary>
		///// Constructs a parser.
		///// </summary>
		///// <param name="options">parsing options</param>
		//public IntegerParser (NumberParseOptions options) : base(options, null) { }

		///// <summary>
		///// Constructs a parser that uses NumberParseOptions.DefaultParseOptions.
		///// </summary>
		///// <param name="ifp">formatting info, or null</param>
		//public IntegerParser (IFormatProvider ifp) : base(NumberParseOptions.None, ifp) { }

		/// <summary>
		/// Constructs a parser.
		/// </summary>
		/// <param name="options">parsing options</param>
		/// <param name="ifp">formatting info, or null</param>
		protected IntegerParser (NumberParseOptions options, IFormatProvider ifp) : base(options, ifp) { }

		//-----------------------------------------------------------------------------
		/// <summary>
		/// Assembles selected tokens from this.Lexer into a normalized number 
		/// string (no digit separators or whitespace).
		/// </summary>
		/// <param name="number">receives the normalized number</param>
		/// <param name="errorMessage">receives an error string (if false is returned)</param>
		/// <returns>true on success, false on error</returns>

		protected override bool AssembleNormalizedNumber (out string number, out string errorMessage)
		{
			number = this.Lexer.DigitsLeft;

			int exponent = 0;
			if (!string.IsNullOrEmpty(this.Lexer.ExponentDigits))
				exponent = int.Parse(this.Lexer.ExponentDigits);

			if (exponent < 0)
			{
				errorMessage = "negative exponents not allowed";
				return false;
			}

			if (!string.IsNullOrEmpty(this.Lexer.DigitsRight))
			{
				if (this.Lexer.DigitsRight.Length >= exponent)
				{
					errorMessage = "invalid fractional digits";
					return false;
				}
				number += this.Lexer.DigitsRight;
				exponent -= this.Lexer.DigitsRight.Length;
			}

			number = number.PadRight(number.Length + exponent, '0');

			if (this.Lexer.IsNegative)
				number = "-" + number;

			errorMessage = null;
			return true;
		}

		//-----------------------------------------------------------------------------
	}
#endif

	//#################################################################################
	/// <summary>
	/// Encapsulates the result of trying to parse a string into an integer type.
	/// </summary>

	public abstract class IntegerParse<T> : NumberParse<T> where T : struct
	{


	}

	//#################################################################################
}
